package com.developer.spoti.vspoti;

import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentParent;
import ohos.agp.components.ComponentTreeObserver;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.StackLayout;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.TouchEvent;

import static ohos.global.configuration.Configuration.DIRECTION_VERTICAL;

/**
 * VspotView
 *
 * @author AnBetter
 * @since 2021-06-22
 */
public class VspotView extends StackLayout implements Component.DrawTask, Component.TouchEventListener {
    private static final float INDICATOR_HEIGHT = 30;
    private static final int NUMTHREE = 3;
    private static final int NUMSIX = 6;
    private static final float NUMFIVE = 5f;
    private static final int NUMCOLOR = 0xdd000000;
    private static final int NUMCIRCLECOLOR = 0xffcccccc;
    private static final int NUMFIFTEEN = 15;
    private static final int NUMMINUSFIFTEEN = -15;
    private static final int NUMTWO = 2;
    private static int mxMessageView = 0;
    private static int myMessageView = 0;
    private static final int MANIMATIONDURATION = 400;
    private float density;
    private Component target;
    private RectFloat rect;
    private VspotMessageView mMessageView;
    private boolean isTop;
    private Gravity mGravity;
    private DismissType dismissType;
    private int marginGuide;
    private boolean mIsShowing;
    private VspotListener mVspotListener;
    private final Paint emptyPaint = new Paint();
    private final Paint paintLine = new Paint();
    private final Paint paintCircle = new Paint();
    private final Paint paintCircleInner = new Paint();
    private final Paint mPaint = new Paint();
    private final Paint targetPaint = new Paint();
    private boolean isNeedDismiss = false;
    private int top;

    private VspotView(Context context, Component view) {
        super(context);
        this.target = view;
        DisplayAttributes attributes = DisplayManager.getInstance().
                getDefaultDisplay(getContext()).get().getRealAttributes();
        density = attributes.densityPixels;
        int[] locationTarget = target.getLocationOnScreen();
        rect = new RectFloat(locationTarget[0], locationTarget[1],
                locationTarget[0] + target.getWidth(), locationTarget[1] + target.getHeight());
        mMessageView = new VspotMessageView(getContext());
        final int padding = (int) (5 * density);
        mMessageView.setPadding(padding, padding, padding, padding);
        mMessageView.setColor(Color.WHITE);
        StackLayout.LayoutConfig layoutParams = new StackLayout.LayoutConfig(
                DirectionalLayout.LayoutConfig.MATCH_CONTENT,
                DirectionalLayout.LayoutConfig.MATCH_CONTENT);
        addComponent(mMessageView, layoutParams);

        getComponentTreeObserver().addWindowBoundListener(new ComponentTreeObserver.WindowBoundListener() {
            @Override
            public void onWindowBound() {
            }

            @Override
            public void onWindowUnbound() {
            }
        });
        if (target.getWidth() == 0) {
            target.setLayoutRefreshedListener(new LayoutRefreshedListener() {
                @Override
                public void onRefreshed(Component component) {
                    VspotView.this.addDrawTask(VspotView.this);
                }
            });
        } else {
            this.addDrawTask(this);
        }

        this.setTouchEventListener(this);
        setClickedListener(component -> {
            if (isNeedDismiss) {
                dismiss();
            }
        });
    }

    /**
     * onDraw
     *
     * @param component
     * @param canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        top = component.getLocationOnScreen()[1];
        setMessageLocation();
        PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
        opts.size = new Size(getWidth(), getHeight());
        opts.pixelFormat = PixelFormat.ARGB_8888;
        opts.editable = true;
        PixelMap pixelMap = PixelMap.create(opts);
        Texture texture = new Texture(pixelMap);
        mPaint.setColor(new Color(NUMCOLOR));
        mPaint.setStyle(Paint.Style.FILL_STYLE);
        mPaint.setAntiAlias(true);
        Canvas tempCanvas = new Canvas(texture);
        tempCanvas.drawRect(canvas.getDeviceClipBounds(), mPaint);
        paintLine.setStyle(Paint.Style.FILL_STYLE);
        paintLine.setColor(Color.WHITE);
        float lineWidth = NUMTHREE * density;
        paintLine.setStrokeWidth(lineWidth);
        paintLine.setAntiAlias(true);
        paintCircle.setStyle(Paint.Style.STROKE_STYLE);
        paintCircle.setColor(Color.WHITE);
        paintCircle.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        float strokeCircleWidth = NUMTHREE * density;
        paintCircle.setStrokeWidth(strokeCircleWidth);
        paintCircle.setAntiAlias(true);
        paintCircleInner.setStyle(Paint.Style.FILL_STYLE);
        paintCircleInner.setColor(new Color(NUMCIRCLECOLOR));
        paintCircleInner.setAntiAlias(true);
        marginGuide = (int) (isTop ? NUMFIFTEEN * density : NUMMINUSFIFTEEN * density);
        float startYlineAndCircle = NumCalcUtil.add(isTop ? rect.bottom : rect.top,marginGuide);
        float index = NumCalcUtil.add(NumCalcUtil.divide(rect.left,NUMTWO),NumCalcUtil.divide(rect.right,NUMTWO));
        float stopY = NumCalcUtil.add(myMessageView,NumCalcUtil.multiply(INDICATOR_HEIGHT,density));
        tempCanvas.drawLine(index, startYlineAndCircle, index, stopY, paintLine);
        float circleSize = NUMSIX * density;
        tempCanvas.drawCircle(index, startYlineAndCircle, circleSize, paintCircle);
        float circleInnerSize = NUMFIVE * density;
        tempCanvas.drawCircle(index, startYlineAndCircle, circleInnerSize, paintCircleInner);
        targetPaint.setBlendMode(BlendMode.CLEAR);
        targetPaint.setAntiAlias(true);
        tempCanvas.drawRoundRect(rect, NUMFIFTEEN, NUMFIFTEEN, targetPaint);
        RectFloat messageViewRect = new RectFloat(mMessageView.getTranslationX(),
                mMessageView.getTranslationY(), NumCalcUtil.add(mMessageView.getTranslationX(),mMessageView.getWidth()),
                NumCalcUtil.add(mMessageView.getTranslationY(),mMessageView.getHeight()));
        tempCanvas.drawRoundRect(messageViewRect, NUMFIFTEEN, NUMFIFTEEN, targetPaint);
        PixelMapHolder holder = new PixelMapHolder(pixelMap);
        RectFloat rectFloat = new RectFloat(0, 0, getWidth(), getHeight());
        canvas.drawPixelMapHolderRect(holder, rectFloat, emptyPaint);
        pixelMap.release();
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        float mXtouch;
        float mYtouch;
        if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            isNeedDismiss = false;
            mXtouch = event.getPointerPosition(event.getIndex()).getX();
            mYtouch = event.getPointerPosition(event.getIndex()).getY();
            switch (dismissType) {
                case outside:
                    if (!isViewContains(mMessageView, mXtouch, mYtouch)) {
                        isNeedDismiss = true;
                    }
                    break;

                case anywhere:
                    isNeedDismiss = true;
                    break;

                case targetView:
                    if (rect.isInclude(mXtouch, mYtouch)) {
                        target.simulateClick();
                        isNeedDismiss = true;
                    }
                    break;
                default:
                    break;
            }
            return true;
        }
        return true;
    }

    /**
     * VSpotListener
     *
     * @since 2021-06-22
     */
    public interface VspotListener {
        /**
         * onDismiss
         *
         * @param view
         */
        void onDismiss(Component view);
    }

    /**
     * Gravity
     *
     * @since 2021-06-22
     */
    public enum Gravity {
        /**
         * auto
         */
        auto, center
    }

    /**
     * DismissType
     *
     * @since 2021-06-22
     */
    public enum DismissType {
        /**
         * outside
         */
        outside, anywhere, targetView
    }

    private int getNavigationBarSize() {
        return 0;
    }

    /**
     * 获取状态栏高度
     *
     * @param context
     * @return int
     */
    private int getStatusBarHeight(Context context) {
        int result = 0;
        Point point1 = new Point();
        Point point2 = new Point();
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        display.getSize(point1);
        display.getRealSize(point2);
        result = (int)NumCalcUtil.subtract(point2.getPointY(),point1.getPointY());
        return result;
    }

    private boolean isLandscape() {
        int mdisplaymode = getContext().getResourceManager().getConfiguration().direction;
        return mdisplaymode != DIRECTION_VERTICAL;
    }

    public boolean isShowing() {
        return mIsShowing;
    }

    /**
     * dismiss
     */
    public void dismiss() {
        removeAllComponents();
        if (mVspotListener != null) {
            mVspotListener.onDismiss(target);
        }

        ComponentParent parent = target.getComponentParent();

        while (parent.getComponentParent() != null) {
            parent = parent.getComponentParent();
        }
        if (parent instanceof ComponentContainer) {
            ((ComponentContainer) parent).removeComponent(this);
        }
        mIsShowing = false;
    }

    /**
     * 获取由本地坐标表示的剪辑边界
     *
     * @param view
     * @param rx
     * @param ry
     * @return boolean
     */
    private boolean isViewContains(Component view, float rx, float ry) {
        int[] location = view.getLocationOnScreen();
        int lx = location[0];
        int ly = location[1];
        int lw = view.getWidth();
        int lh = view.getHeight();

        return !(rx < lx || rx > lx + lw || ry < ly || ry > ly + lh);
    }

    void setMessageLocation() {
        Point point = resolveMessageViewLocation();
        mMessageView.setTranslationX(point.getPointX());
        mMessageView.setTranslationY(point.getPointY());
        postLayout();
    }

    private Point resolveMessageViewLocation() {
        int[] locationTarget1 = target.getLocationOnScreen();
        rect = new RectFloat(locationTarget1[0], locationTarget1[1] - top,
                locationTarget1[0] + target.getWidth(), locationTarget1[1] + target.getHeight() - top);
        if (mGravity == Gravity.center) {
            mxMessageView = (int) NumCalcUtil.add(NumCalcUtil.subtract(rect.left,
                    NumCalcUtil.divide(mMessageView.getWidth(),NUMTWO)),NumCalcUtil.divide(target.getWidth(),NUMTWO));
        } else {
            mxMessageView = (int) (rect.right) - mMessageView.getWidth();
        }
        if (isLandscape()) {
            mxMessageView -= getNavigationBarSize();
        }

        if (mxMessageView + mMessageView.getWidth() > getWidth()) {
            mxMessageView = getWidth() - mMessageView.getWidth();
        }

        if (mxMessageView < 0) {
            mxMessageView = 0;
        }

        if (NumCalcUtil.add(rect.top,NumCalcUtil.multiply(INDICATOR_HEIGHT,density))
                > NumCalcUtil.divide(getHeight(),NUMTWO)) {
            isTop = false;
            myMessageView = (int) NumCalcUtil.subtract(NumCalcUtil.subtract(rect.top,
                    mMessageView.getHeight()),NumCalcUtil.multiply(INDICATOR_HEIGHT,density));
        } else {
            isTop = true;
            myMessageView = (int) NumCalcUtil.add(NumCalcUtil.add(rect.top,
                    target.getHeight()),NumCalcUtil.multiply(INDICATOR_HEIGHT,density));
        }

        if (myMessageView < 0) {
            myMessageView = 0;
        }
        return new Point(mxMessageView, myMessageView);
    }

    /**
     * show
     *
     * @author AnBetter
     * @since 2021-06-22
     */
    public void show() {
        LayoutConfig layoutParams = new LayoutConfig(DirectionalLayout.
                LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_PARENT);
        setLayoutConfig(layoutParams);

        ComponentParent parent = target.getComponentParent();

        while (parent.getComponentParent() != null) {
            parent = parent.getComponentParent();
        }
        if (parent instanceof ComponentContainer) {
            ((ComponentContainer) parent).addComponent(this);
        }

        AnimatorValue startAnimation = new AnimatorValue();
        startAnimation.setDuration(MANIMATIONDURATION);
        startAnimation.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                setAlpha(v);
            }
        });
        startAnimation.start();
        mIsShowing = true;
    }

    /**
     * setTitle
     *
     * @param str
     */
    public void setTitle(String str) {
        mMessageView.setTitle(str);
    }

    /**
     * setContentText
     *
     * @param str
     */
    public void setContentText(String str) {
        mMessageView.setContentText(str);
    }

    /**
     * setTitleTypeFace
     *
     * @param font
     */
    public void setTitleTypeFace(Font font) {
        mMessageView.setTitleTypeFace(font);
    }

    /**
     * setContentTypeFace
     *
     * @param font
     */
    public void setContentTypeFace(Font font) {
        mMessageView.setContentTypeFace(font);
    }

    /**
     * setTitleTextSize
     *
     * @param size
     */
    public void setTitleTextSize(int size) {
        mMessageView.setTitleTextSize(size);
    }

    /**
     * setContentTextSize
     *
     * @param size
     */
    public void setContentTextSize(int size) {
        mMessageView.setContentTextSize(size);
    }

    /**
     * Builder
     *
     * @since 2021-06-22
     */
    public static class Builder {
        private Component targetView;
        private String title;
        private String contentText;
        private Gravity gravity;
        private DismissType dismissType;
        private Context context;
        private int titleTextSize;
        private int contentTextSize;
        private Font titleTypeFace;
        private Font contentTypeFace;
        private VspotListener vspotListener;

        /**
         * Builder
         *
         * @param context
         */
        public Builder(Context context) {
            this.context = context;
        }

        /**
         * setTargetView
         *
         * @param view
         * @return Builder
         */
        public Builder setTargetView(Component view) {
            this.targetView = view;
            return this;
        }

        /**
         * setGravity
         *
         * @param gravity
         * @return Builder
         */
        public Builder setGravity(Gravity gravity) {
            this.gravity = gravity;
            return this;
        }

        /**
         * setTitle
         *
         * @param title
         * @return Builder
         */
        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }

        /**
         * setContentText
         *
         * @param contentText
         * @return Builder
         */
        public Builder setContentText(String contentText) {
            this.contentText = contentText;
            return this;
        }

        /**
         * setContentTypeFace
         *
         * @param font
         * @return Builder
         */
        public Builder setContentTypeFace(Font font) {
            this.contentTypeFace = font;
            return this;
        }

        /**
         * setVspotListener
         *
         * @param vspotListener
         * @return Builder
         */
        public Builder setVspotListener(VspotListener vspotListener) {
            this.vspotListener = vspotListener;
            return this;
        }

        /**
         * setTitleTypeFace
         *
         * @param font
         * @return Builder
         */
        public Builder setTitleTypeFace(Font font) {
            this.titleTypeFace = font;
            return this;
        }

        /**
         * the defined text size overrides any defined size in the default or provided style
         *
         * @param size title text by sp unit
         * @return builder
         */
        public Builder setContentTextSize(int size) {
            this.contentTextSize = size;
            return this;
        }

        /**
         * the defined text size overrides any defined size in the default or provided style
         *
         * @param size title text by sp unit
         * @return builder
         */
        public Builder setTitleTextSize(int size) {
            this.titleTextSize = size;
            return this;
        }

        /**
         * setDismissType
         *
         * @param dismissType
         * @return Builder
         */
        public Builder setDismissType(DismissType dismissType) {
            this.dismissType = dismissType;
            return this;
        }

        /**
         * VspotView
         *
         * @return VspotView
         */
        public VspotView build() {
            VspotView vspotView = new VspotView(context, targetView);
            vspotView.mGravity = gravity != null ? gravity : Gravity.auto;
            vspotView.dismissType = dismissType != null ? dismissType : DismissType.targetView;

            vspotView.setTitle(title);
            if (contentText != null) {
                vspotView.setContentText(contentText);
            }
            if (titleTextSize != 0) {
                vspotView.setTitleTextSize(titleTextSize);
            }
            if (contentTextSize != 0) {
                vspotView.setContentTextSize(contentTextSize);
            }
            if (titleTypeFace != null) {
                vspotView.setTitleTypeFace(titleTypeFace);
            }
            if (contentTypeFace != null) {
                vspotView.setContentTypeFace(contentTypeFace);
            }
            if (vspotListener != null) {
                vspotView.mVspotListener = vspotListener;
            }
            return vspotView;
        }
    }
}

